Go学习圣经:队列削峰+批量写入 超高并发原理和实操 您所在的位置:网站首页 go语言圣经 pdf Go学习圣经:队列削峰+批量写入 超高并发原理和实操

Go学习圣经:队列削峰+批量写入 超高并发原理和实操

2023-11-06 10:07| 来源: 网络整理| 查看: 265

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领 免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领

免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取

GO 学习圣经:底层原理和实操 说在前面:

本文是《Go学习圣经》 的第二部分。

第一部分请参见:Go学习圣经:0基础精通GO开发与高并发架构(1)

现在拿到offer超级难,甚至连面试电话,一个都搞不到。

尼恩的技术社群中(50+),很多小伙伴凭借 “左手云原生+右手大数据”的绝活,拿到了offer,并且是非常优质的offer,据说年终奖都足足18个月。

从Java高薪岗位和就业岗位来看,云原生、K8S、GO 现在对于 高级工程师/架构师来说,越来越重要。尼恩从架构师视角出发,基于自己的尼恩 3高架构师知识体系和知识宇宙,写一本《GO学习圣经》

最终的学习目标

咱们的目标,不仅仅在于 GO 应用编程自由,更在于 GO 架构自由。

前段时间,一个2年小伙伴希望涨薪到18K, 尼恩把GO 语言的项目架构,给他写入了简历,导致他的简历金光闪闪,脱胎换股,完全可以去拿头条、腾讯等30K的offer, 年薪可以直接多 20W。

足以说明,GO 架构的含金量。

另外,前面尼恩的云原生是没有涉及GO的,但是,没有GO的云原生是不完整的。

所以, GO语言、GO架构学习完了之后,咱们在去打个回马枪,完成云原生的第二部分: 《Istio + K8S CRD的架构与开发实操》 , 帮助大家彻底穿透云原生。

本书目录

目录GO 学习圣经:底层原理和实操说在前面:最终的学习目标本书目录并发编程Go 协程创建Go 协程(Goroutine)Go标准库 协程同步Mutex互斥锁同步WaitGroup 等待组Cond(条件变量)channel 通道channel 通道(/信道)的两个基本操作附录:make 函数如何使用?range遍历 和 通道关闭 closeclose Channel 的一些说明多通道查询select 语句/通道的多路复用Go的select 和 OS的select 对比非阻塞查询带缓冲的通道Java BlockingQueue 和 Go channel 对比学习SynchronousQueue VS 无缓冲channelLinkedBlockingQueue VS 缓冲通道 buffered channel操作对比go rocketmq 编程实例:使用 RocketMQ 的 Go 客户端来发送和接收消息消息发送和接受的验证启动 rocketmq启动 go 实例附录:Go 模块的安装和使用启用 Go 模块创建新项目go.mod安装第三方库下载依赖项管理依赖项GoLand 中使用 Go 模块(go mod)管理依赖项打开或创建一个 Go 项目添加依赖项解决require内依赖全部飘红问题管理依赖关系构建和运行项目gorm 操作mysql什么是ORM?gorm介绍gorm安装gorm模型定义3、gorm模型标签定义表名gorm.Model自动更新时间gorm连接数据库1、配置DSN (Data Source Name)2、使用gorm.Open连接数据库3、gorm调试模式4、gorm连接池CRUD操作插入数据查询数据更新数据删除数据go与mysql数据类型关系mysql日期时间格式go 存储 mysql TIMESTAMP格式高并发实操: 消息队列削峰解耦+ 批量写入DB为什么要使用消息队列削峰解耦异步实操:用GO实现消息队列削峰解耦为什么应该使用批量插入来提高MySQL性能?实操:用GO实现批量写入执行效果Golang GC垃圾回收器Cache 和 Buffer的区别Golang GC发展史还有 5W字待发布参考资料技术自由的实现路径:实现你的 架构自由:实现你的 响应式 自由:实现你的 spring cloud 自由:实现你的 linux 自由:实现你的 网络 自由:实现你的 分布式锁 自由:实现你的 王者组件 自由:实现你的 面试题 自由:免费获取11个技术圣经PDF:

并发编程

Go 将并发结构作为核心语言的一部分提供。

Go 协程

Go 协程(Goroutine)是 Go 语言中的一种轻量级线程实现。

Go 协程(Goroutine)通过在单个线程内同时运行多个函数来实现并发,从而避免了线程切换的开销,并且能够更加高效地利用系统资源。

与传统的线程模型不同,Go 协程不是由操作系统内核调度的,而是由 Go 运行时(runtime)自己调度的。

为啥是轻量级线程呢?Go 协程(Goroutine)可以避免因为线程调度引起的额外开销,并且能够更好地控制协程的数量和调度机制。

创建一个协程非常简单,只需要在函数调用前面添加 go 关键字即可,例如:

func main() { go func() { fmt.Println("Hello, world!") }() }

这段代码会创建一个新的协程,并在其中执行匿名函数中的代码。

这个协程会在后台运行,不会阻塞主线程的执行。

创建Go 协程(Goroutine)

Go 程(goroutine)是由 Go 运行时管理的轻量级线程。

创建一个协程非常简单,只需要在函数调用前面添加 go 关键字即可

go f(x, y, z)

上面的代码,会启动一个新的 Go 协程(Goroutine)去执行 f(x, y, z) 函数, x, y 和 z 的求值发生在当前的 Go协 程中,而 f 的执行发生在新的 Go 协程中。

下面是一个例子

package cocurrent import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Printf("字符 %s: %d \n", s, i) } } func GoroutineDemo() { go say("sync world ") say("hello") }

执行的结果

Go 协程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步。

Go标准库 协程同步

Go 标准库中提供了多种同步机制,可以满足不同场景下的需求。以下是 Go 中常用的同步机制:

Mutex:互斥锁,用于保护临界区(critical section)代码,只允许一个协程进入临界区执行代码,其他协程需要等待。使用 sync.Mutex 类型来定义互斥锁。 RWMutex:读写锁,用于保证在读操作时允许多个协程同时访问资源,在写操作时只允许一个协程进入临界区修改资源。使用 sync.RWMutex 类型来定义读写锁。 WaitGroup:等待组,用于等待一组并发协程执行完成后再继续执行。使用 sync.WaitGroup 类型来定义等待组。 Cond:条件变量,用于在协程之间同步和通信。使用 sync.Cond 类型来定义条件变量。 Once:单次执行,用于确保某个操作只会被执行一次。使用 sync.Once 类型来定义单次执行。

这些同步机制都可以帮助我们更好地控制协程的执行顺序和并发访问共享资源的安全性。在实际开发中,我们需要根据具体情况选择合适的同步机制,并且要注意避免死锁等问题。

Mutex互斥锁同步

这里涉及的概念叫做 互斥(mutual exclusion) ,我们通常使用互斥锁(Mutex)这一数据结构来提供这种机制。

Go 中的 Mutex(互斥锁)是一种最基本的同步机制,用于保护临界区代码,只允许一个协程进入临界区执行代码,其他协程需要等待。在 Go 标准库中,可以使用 sync.Mutex 类型来定义互斥锁。

Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:

Lock

Unlock

我们可以通过在代码前调用 Lock 方法,在代码后调用 Unlock 方法来保证一段代码的互斥执行。参见 Inc 方法。

我们也可以用 defer 语句来保证互斥锁一定会被解锁。参见 Value 方法。

sync.Mutex类似于java 里边的 Lock 显示锁。 关于java显示锁,请参见 尼恩《Java 高并发核心编程 卷2 加强版》

啰嗦一下,sync.Mutex 类型包含两个方法:

Lock():获得互斥锁,如果当前锁已经被其他协程获得,就会一直等待,直到锁被释放为止。 Unlock():释放互斥锁,允许其他协程获得锁并进入临界区。

下面是一个使用 Mutex 实现协程同步的例子:

import ( "fmt" "sync" ) var counter int func MutexDemo() { var wg sync.WaitGroup var mu sync.Mutex wg.Add(100) for i := 0; i < 100; i++ { go func() { mu.Lock() counter++ mu.Unlock() wg.Done() }() } wg.Wait() fmt.Println("Counter:", counter) }

在这个例子中,我们创建了一个计数器 counter,并启动了 100 个协程对其进行累加操作。由于对 counter 的访问是并发的,因此需要使用互斥锁 mu 来保护它,以避免不同协程之间的竞争条件。

在每个协程中,首先使用 mu.Lock() 方法获得互斥锁,然后对 counter 进行加 1 操作,并最终使用 mu.Unlock() 方法释放互斥锁。由于只有一个协程可以同时获得互斥锁并进入临界区,因此可以保证对 counter 的操作是安全的。

最后,我们使用 sync.WaitGroup 来等待所有协程执行完毕,并输出最终的计数器值。

WaitGroup 等待组

在 Go 中,可以使用 sync.WaitGroup 来等待一组协程完成执行。

sync.WaitGroup 类似于java 里边的闭锁。 关于java闭锁,请参见 尼恩《Java 高并发核心编程 卷2 加强版》

sync.WaitGroup 类型提供了三个方法:

Add(delta int):将 WaitGroup 的计数器加上 delta 值。如果 delta 是负数,则会 panic。 Done():将 WaitGroup 的计数器减 1。相当于 Add(-1)。 Wait():阻塞当前协程,直到 WaitGroup 的计数器为 0。

下面是一个使用 sync.WaitGroup 实现并发下载的例子:

import ( "fmt" "sync" ) func main() { urls := []string{ "https://www.google.com", "https://www.bing.com", "https://www.yahoo.com", "https://www.baidu.com", "https://www.amazon.com", "https://www.apple.com", } var wg sync.WaitGroup for _, url := range urls { wg.Add(1) go func(url string) { defer wg.Done() download(url) }(url) } wg.Wait() fmt.Println("All downloads completed.") } func download(url string) { fmt.Printf("Downloading %s...\n", url) // 模拟下载操作 }

在这个例子中,我们定义了一个 urls 列表,包含了需要下载的网址。

然后创建了一个 sync.WaitGroup 对象 wg,并通过调用 wg.Add(1) 把计数器置为 1。

接着使用 for 循环遍历 urls 列表,对每个网址都启动一个新的协程,并在协程中调用 download() 函数来下载网页内容。

在协程中,通过 defer wg.Done() 将 WaitGroup 的计数器减 1,表示当前协程已经完成了下载任务。

最后,主程序调用 wg.Wait() 来等待所有协程执行完毕,并输出提示信息表示所有下载任务都已经完成了。

Cond(条件变量)

Go 中的 Cond(条件变量)是一种同步机制,用于在协程之间同步和通信。

Cond 是基于 Mutex 和 WaitGroup 实现的,它可以让一个或多个协程等待某个条件满足后再执行下一步操作。

在 Go 标准库中,可以使用 sync.Cond 类型来定义条件变量。

sync.Cond 类型包含三个方法:

Broadcast():唤醒所有正在等待条件变量的协程。 Signal():唤醒一个正在等待条件变量的协程。 Wait():阻塞当前协程,并解锁 Mutex,直到收到 Broadcast 或 Signal 信号后才会被唤醒并重新获得 Mutex。

下面是一个使用 Cond 实现生产者-消费者模型的例子:

import ( "fmt" "sync" ) const capacity = 5 var queue []int var mu sync.Mutex var cond = sync.NewCond(&mu) func main() { var wg sync.WaitGroup wg.Add(2) // 生产者协程 go func() { defer wg.Done() for i := 0; i < capacity*2; i++ { mu.Lock() for len(queue) == capacity { cond.Wait() } queue = append(queue, i) fmt.Println("Produce:", i) if len(queue) == 1 { cond.Signal() } mu.Unlock() } }() // 消费者协程 go func() { defer wg.Done() for i := 0; i < capacity*2; i++ { mu.Lock() for len(queue) == 0 { cond.Wait() } item := queue[0] queue = queue[1:] fmt.Println("Consume:", item) if len(queue) == capacity-1 { cond.Signal() } mu.Unlock() } }() wg.Wait() }

在这个例子中,我们定义了一个长度为 5 的队列,然后创建了两个协程,一个用来生产数据,另一个用来消费数据。

在协程中,使用 sync.Mutex 和 sync.Cond 对象来保护和同步共享资源。

在生产者协程中,首先调用 mu.Lock() 获取互斥锁,然后使用 for 循环判断队列是否已满,

如果已满则调用 cond.Wait() 阻塞当前协程,等待消费者协程唤醒。 如果队列未满,则将数据插入队列并打印生产的数据。

在插入数据后,如果队列原来为空,则调用 cond.Signal() 唤醒一个正在等待条件变量的协程。最后,使用 mu.Unlock() 释放互斥锁。

在消费者协程中,首先调用 mu.Lock() 获取互斥锁,然后使用 for 循环判断队列是否为空

如果为空则调用 cond.Wait() 阻塞当前协程,等待生产者协程唤醒。 如果队列非空,则取出队头元素并打印消费的数据。

在取出数据后,如果队列原来已满,则调用 cond.Signal() 唤醒一个正在等待条件变量的协程。

最后,使用 mu.Unlock() 释放互斥锁。

channel 通道

除了标准库 sync 包提供了协程 同步能力,还可以使用channel 来实现。

channel 是一种特殊的数据类型,可以用来在协程之间传递数据,并且能够实现阻塞式等待和唤醒功能。

channel 通道(/信道)的两个基本操作

和映射与切片一样,channel 通道在使用前必须创建:

ch := make(chan int)

使用 make 函数创建 channel 时,第一个参数为 channel 类型,第二个参数为缓冲区大小(可选)。注意,第二个参数是可选的。

channel 通道在创建的时候, 类型参数表示 通道里边 值的类型。所以,通道是带有类型的管道,你可以通过它用信道操作符



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有